package edu.unika.aifb.graphindex.searcher.keyword.evaluation;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;

import edu.unika.aifb.graphindex.model.IEntity;
import edu.unika.aifb.graphindex.query.QNode;
import edu.unika.aifb.graphindex.query.QueryGraph;

public class Row implements Comparable<Row>, Cloneable{
	private HashMap<QNode, IEntity> nodeToEntity = new HashMap<QNode, IEntity>();
	private QueryGraph graph;
	private QNode nextNode;
	private QNode lastNode;
	private double score = 0;
	
	public Row(Set<QNode> nodes, QueryGraph graph){
		for (QNode node : nodes) {
			nodeToEntity.put(node, null);
		}
		this.graph = graph;
	}
	
	private Row(HashMap<QNode, IEntity> nodeToEntity){
		this.nodeToEntity = nodeToEntity;
	}
	
	public void addEntity(QNode node, IEntity entity){
		nodeToEntity.put(node, entity);
		this.lastNode = node;
		setNextNode();
		updateScore();
	}
	
	private void updateScore(){
		score = CombineScore.combineEntityScore(nodeToEntity);
	}
	
	private void setNextNode(){
		if(!isComplete()){
			for(QNode successorNode : graph.successors(lastNode)){
				if(nodeToEntity.get(successorNode) == null){
					nextNode = successorNode;
					return;
				}
			}
			
			for(QNode predecessorNode : graph.predecessors(lastNode)){
				if(nodeToEntity.get(predecessorNode) == null){
					nextNode = predecessorNode;
					return;
				}
			}
			
			for(QNode qnode : nodeToEntity.keySet()){
				if(nodeToEntity.get(qnode) != null){
					for(QNode successorNode : graph.successors(qnode)){
						if(nodeToEntity.get(successorNode) == null){
							lastNode = qnode;
							nextNode = successorNode;
							return;
						}
					}
					
					for(QNode predecessorNode : graph.predecessors(qnode)){
						if(nodeToEntity.get(predecessorNode) == null){
							lastNode = qnode;
							nextNode = predecessorNode;
							return;
						}
					}
				}
			}
		}
	}
	
	
	public QNode getNextNode() {
		return nextNode;
	}
	
	public QNode getLastNode() {
		return lastNode;
	}

	public boolean isComplete(){
		for(QNode node : nodeToEntity.keySet())
			if(nodeToEntity.get(node) == null)
				return false;
		return true;
	}
	
	public double getScore(){
		return score;
	}
	
	public HashMap<QNode, IEntity> getNodeToEntity(){
		return nodeToEntity;
	}

	@Override
	public int compareTo(Row o) {
		double score = getScore();
		double otherScore = o.getScore();
		if(score > otherScore)
			return -1;
		if(score < otherScore)
			return 1;
		return toString().compareTo(o.toString());
	}
	
	public String toString(){
		StringBuffer sb = new StringBuffer();
		StringBuffer terms = new StringBuffer();
		for(QNode node : nodeToEntity.keySet()){
			IEntity entity = nodeToEntity.get(node);
			if(entity != null){
				if(!node.getLabel().startsWith("db"))
					sb.append(entity.toString());
				if(entity.getTerms() != null){
					if(entity.getTerms().length > 20)
						terms.append(Arrays.toString(entity.getTerms()).substring(0, 20)).append("] ");
					else
						terms.append(Arrays.toString(entity.getTerms()));
				}
				sb.append(" ");
			}else
				sb.append(node.getLabel()).append(" = null ");
		}
		sb.append(" # ").append(terms).append(" ").append(getScore());
		return sb.toString();
	}
	
	@SuppressWarnings("unchecked")
	public Row clone(){
		Row clonedRow = new Row((HashMap<QNode, IEntity>)nodeToEntity.clone());
		clonedRow.setGraph(graph);
		clonedRow.setNextNode(nextNode);
		clonedRow.setLastNode(lastNode);
		clonedRow.setScore(score);
		return clonedRow;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof Row))
			return false;
		Row otherRow = (Row)obj;
		
		return nodeToEntity.equals(otherRow.getNodeToEntity());
	}

	@Override
	public int hashCode() {
		return nodeToEntity.hashCode();
	}

	public void clear(){
		nodeToEntity.clear();
	}

	public void setGraph(QueryGraph graph) {
		this.graph = graph;
	}

	public void setNextNode(QNode nextNode) {
		this.nextNode = nextNode;
	}

	public void setLastNode(QNode lastNode) {
		this.lastNode = lastNode;
	}

	public void setScore(double score) {
		this.score = score;
	}
	
	
	
}
